/*
 * NativeRobot.java
 * 
 * Copyright (C) 2007 Francisco J. Hernández
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * 
 * NativeRobot.java
 */


package NativeRobot;

import java.awt.*;
import java.awt.event.InputEvent;
import java.io.*;
import java.util.HashMap;

/**
 * Clase que extiende la clase java.awt.Robot, para a�adir las funcionalidades con las
 *      que se pueda obtener el cursor del raton en cualquier momento de la ejecucion
 *      aunque el puntero no este dentro de nuestra ventana, tambi� averigua que boton
 *      esta pulsado en que momento y que tecla esta pulsada.
 *
 *      Hace uso de codigo nativo por lo que no es multiplataforma, concretamente solo 
 *      funciona bajo windows, si alguien pudiese implementar la libreria dinamica para
 *      las X-Windows siguiendo el esquema de entrada salida adecuada, se agradeceria.
 *
 * @author Francisco J. Hernández
 */
public class NativeRobot extends Robot
{
    /**
     * Crea una nueva instancia de NativeRobot
     */
    public NativeRobot() throws AWTException
    {
        loadNativeLibrary();
    }

    /**
     * Crea una nueva instancia de NativeRobot
     */
    public NativeRobot(GraphicsDevice screen)
        throws AWTException
    {
        super(screen);
        loadNativeLibrary();
    }

    /**
     * Crea una nueva instancia de NativeRobot
     */
    public NativeRobot(String windowsTittle) throws AWTException {
        loadNativeLibrary();
        if(findAllTittleWindow(windowsTittle))
            System.out.println("Ventana \"" + actWinTittle + "\" encontrada\nModo Nativo activado por defecto");
        else
            System.err.println("WARNING: No se encontro ventana \"" + windowsTittle + "\"\nModo nativo no esta funcionando");
    }

    /**
     * Carga la libreria dinamica con el codigo nativo adecuado al sistema
     */
    private void loadNativeLibrary()
    {
        java.io.InputStream in;
        File fich;
        in = getClass().getResourceAsStream(NATIVE_LIBRARY);
        fich = new File(System.getProperty("java.io.tmpdir") + NATIVE_LIBRARY);
        //this;
        if(!isNative())
        {
            System.err.println("ATENCI�N: para esta plataforma no hay una libreria nativa implementada");
            return;
        }
        try
        {
            if(fich.exists())
                fich.delete();
            if(!fich.exists())
            {
                DataInputStream a = new DataInputStream(in);
                DataOutputStream b = new DataOutputStream(new FileOutputStream(fich));
                byte buff[] = new byte[a.available()];
                a.readFully(buff);
                b.write(buff);
                a.close();
                b.close();
            }
            Runtime.getRuntime().load(fich.getCanonicalPath().toString());
        }
        catch(IOException ex)
        {
            ex.printStackTrace();
        }
        return;
    }

    public Color getPixelColor(int x, int y)
    {
        Color color = null;
        if(actWin && nativeMode)
            try
            {
                int tmp[] = nativeGetPixelColor(actWinHandle, x, y);
                color = new Color(tmp[0], tmp[1], tmp[2]);
            }
            catch(Exception e)
            {
                actWin = false;
                color = super.getPixelColor(x, y);
            }
        else
            color = super.getPixelColor(x, y);
        return color;
    }

    private native int[] nativeGetPixelColor(int actWinHandle, int x, int y);

    public void keyPress(int keyCode)
    {
        if(actWin && nativeMode)
            try
            {
                nativeKeyPress(actWinHandle, keyCode);
                super.delay(super.getAutoDelay());
            }
            catch(Exception e)
            {
                actWin = false;
                super.keyPress(keyCode);
            }
        else
            super.keyPress(keyCode);
    }

    private native boolean nativeKeyPress(int actWinHandle, int keyCode);

    public void keyRelease(int keyCode)
    {
        if(actWin && nativeMode)
            try
            {
                nativeKeyRelease(actWinHandle, keyCode);
                super.delay(super.getAutoDelay());
            }
            catch(Exception e)
            {
                actWin = false;
                super.keyRelease(keyCode);
            }
        else
            super.keyRelease(keyCode);
    }

    private native boolean nativeKeyRelease(int i, int j);

    public void mouseMove(int x, int y)
    {
        if(actWin && nativeMode)
            try
            {
                nativeMouseMove(actWinHandle, x, y);
                super.delay(super.getAutoDelay());
            }
            catch(Exception e)
            {
                actWin = false;
                super.mouseMove(x, y);
            }
        else
            super.mouseMove(x, y);
    }

    private native boolean nativeMouseMove(int i, int j, int k);

    public void mousePress(int button)
    {
        if(actWin && nativeMode)
            try
            {
                switch(button)
                {
                case InputEvent.BUTTON1_MASK: // '\020'
                    nativeLeftMousePress(actWinHandle);
                    super.delay(super.getAutoDelay());
                    break;

                case InputEvent.BUTTON2_MASK: // '\b'
                    nativeRightMousePress(actWinHandle);
                    super.delay(super.getAutoDelay());
                    break;

                case InputEvent.BUTTON3_MASK: // '\004'
                    nativeMiddleMousePress(actWinHandle);
                    super.delay(super.getAutoDelay());
                    break;

                default:
                    super.mousePress(button);
                    break;
                }
            }
            catch(Exception e)
            {
                actWin = false;
                super.mousePress(button);
            }
        else
            super.mousePress(button);
    }

    private native boolean nativeLeftMousePress(int i);

    private native boolean nativeMiddleMousePress(int i);

    private native boolean nativeRightMousePress(int i);

    public void mouseRelease(int button)
    {
        if(actWin && nativeMode)
            try
            {
                switch(button)
                {
                case InputEvent.BUTTON1_MASK: // '\020'
                    nativeLeftMouseRelease(actWinHandle);
                    super.delay(super.getAutoDelay());
                    break;

                case InputEvent.BUTTON2_MASK: // '\b'
                    nativeRightMouseRelease(actWinHandle);
                    super.delay(super.getAutoDelay());
                    break;

                case InputEvent.BUTTON3_MASK: // '\004'
                    nativeMiddleMouseRelease(actWinHandle);
                    super.delay(super.getAutoDelay());
                    break;

                default:
                    super.mouseRelease(button);
                    break;
                }
            }
            catch(Exception e)
            {
                actWin = false;
                super.mouseRelease(button);
            }
        else
            super.mouseRelease(button);
    }

    private native boolean nativeLeftMouseRelease(int i);

    private native boolean nativeMiddleMouseRelease(int i);

    private native boolean nativeRightMouseRelease(int i);

    public boolean findAllTittleWindow(String subTittle)
    {
        return findAllTittleWindow(subTittle, false);
    }

    public boolean findAllTittleWindow(String subTittle, boolean caseInsensitive)
    {
        if (caseInsensitive)
            subTittle = subTittle.toLowerCase();
        int desc[] = getNativeAllWindowHandle();
        CharSequence seq = subTittle.subSequence(0, subTittle.length());
        ventNativas.clear();
        for(int i = 0; i < desc.length; i++)
        {
            byte tmpCad[] = getNativeWindowTittle(desc[i]);
            if (tmpCad == null)
                continue;
            String tmpStr = new String(tmpCad);
            if (caseInsensitive)
                tmpStr = tmpStr.toLowerCase();
            if(tmpStr.contains(seq))
            {
                actWinTittle = tmpStr;
                actWinHandle = desc[i];
                actWin = true;
                ventNativas.put(tmpStr, new Integer(desc[i]));
            }
        }

        if(!ventNativas.isEmpty())
        {
            return true;
        } else
        {
            actWin = false;
            actWinTittle = null;
            actWinHandle = 0;
            return false;
        }
    }
    
    private native byte[] getNativeWindowTittle(int i);

    private native int[] getNativeAllWindowHandle();

    private native int[] getNativeMousePos();

    private native int[] getNativeMousePosHandle(int i);

    private native int isNativeButtonPressed(int i);

    private native int isNativeKeyPressed(int i);

    public Point getMousePos()
    {
        int coor[];
        if(actWin && nativeMode)
            coor = getNativeMousePosHandle(actWinHandle);
        else
            coor = getNativeMousePos();
        return new Point(coor[0], coor[1]);
    }

    public boolean isKeyPressed(int codeKey)
    {
        return isNativeKeyPressed(codeKey) != 0;
    }

    public boolean isButtonPressed(int codeButton)
    {
        return isNativeButtonPressed(codeButton) != 0;
    }

    public static boolean isWindowSystem()
    {
        return windowSystem;
    }

    public static boolean isNative()
    {
        return windowSystem;
    }

    public static HashMap getVentNativas()
    {
        return ventNativas;
    }

    public static String getActWinTittle()
    {
        return actWinTittle;
    }

    public static void setActWinTittle(String aActWinTittle)
    {
        Integer tmp = (Integer)ventNativas.get(aActWinTittle);
        if(tmp != null)
        {
            actWinHandle = tmp.intValue();
            actWinTittle = aActWinTittle;
            actWin = true;
            return;
        } else
        {
            return;
        }
    }

    public static int getActWinHandle()
    {
        return actWinHandle;
    }

    public static boolean isNativeMode()
    {
        return nativeMode;
    }

    public static void setNativeMode(boolean aActNative)
    {
        nativeMode = aActNative;
    }

    public static boolean isActWin()
    {
        return actWin;
    }

    private static boolean windowSystem = false;
    private static boolean actWin = false;
    private static boolean nativeMode = true;
    static final String NATIVE_LIBRARY = "WNativeRobot.dll";
    private static HashMap ventNativas = new HashMap();
    private static String actWinTittle = null;
    private static int actWinHandle;

    static 
    {
        if(System.getProperty("os.name").toLowerCase().startsWith("windows"))
            windowSystem = true;
        else
            windowSystem = false;
    }
}